class Obj
  def initialize(val = 1)
    @wwww = val
  end
  attr_reader :wwww
  attr_writer :wwww
end

class PPP
  def initialize(val = 'text', objobj = [Obj.new,Obj.new,Obj.new])
    @pppp = val
    @qqqq = objobj
  end
  attr_reader :pppp, :qqqq
  attr_writer :pppp, :qqqq
end

class Product
  def initialize(ppobj = PPP.new,attr2 = 1, attr3 = [1,2,3,4,5])
    @attr1 = ppobj
    @attr2 = attr2
    @attr3 = attr3
  end
  attr_reader :attr1, :attr2, :attr3
  attr_writer :attr1, :attr2, :attr3
end

class NewProduct
  def initialize
    @product1 = Product.new
    @product2 = Product.new(PPP.new('text2'),2,[6,7,8,9,0])
  end
  attr_reader :product1, :product2
  attr_writer :product1, :product2
  
  def deep_clone  
    Marshal::load(Marshal.dump(self))  
  end
end
 

# *Description*
# * Gives the difference between two objects of the same class
# *Parameters*
# *  Object1: First Object
# *  Object2: Second Object
# *Returns*
# * object_diff : ON SUCCESS : Array of strings with differences
# * nil              : ON SUCCESS : When objects are identical
# * false           : ON FAILURE : When objects are not instances of the same class
# *Exception Raises*
# *   No Exception

def objectdiff(object1, object2, attr) #returns nil if objects are identical, #returns false if objects are not the instance of same class
  return false if object1.class != object2.class # Objects are instances of the same class or not!
  object_diff = []
    object1 == object2 ? response = true : response = false
    if response != true
      change = Hash.new
      change.store(:new,object2)
      change.store(:old,object1)
      change.store(:attr, attr)
      object_diff << change
    end
  object_diff
end

Changes = []

def audit(object1, object2, var = '')
  variables = object1.instance_variables
  for i in 0...variables.length
    obj1var = eval("object1.#{strip_at(variables[i])}")
    obj2var = eval("object2.#{strip_at(variables[i])}")
    obj1varclass = eval("object1.#{strip_at(variables[i])}.class.to_s")
    obj2varclass = eval("object2.#{strip_at(variables[i])}.class.to_s")

    if((eval("object1.#{variables[i].delete '@'}.class.to_s") == 'String') || (eval("object1.#{variables[i].delete '@'}.class.to_s") == 'Fixnum'))
      Changes << objectdiff(obj1var, obj2var, var + '/' + variables[i].to_s)
    else 
      if(obj1varclass == 'Hash')
        obj1var = obj1var.to_a
        obj2var = obj2var.to_a
      end
      if(obj1varclass == 'Array') #Object belongs to 'Array' class
        if(obj1var[0].class.to_s == 'Fixnum' || obj1var[0].class.to_s == 'String')  # Its an array of either Strings or Fixnums
          for count in 0...variables[i].size
            Changes << objectdiff(obj1var[count], obj2var[count], var + '/' + variables[i].to_s)
          end
        else #it is array of objects of un-atomic class
          for count in 0...variables[i].size
            audit(obj1var[count], obj2var[count], var + '/' + variables[i].to_s + '/' + count.to_s)
          end
        end
      else # it is not an array
        audit(obj1var, obj2var, var + '/' + variables[i].to_s)
      end
    end
  end
  Changes
end

def strip_at(string)
  string = string.to_s
  if string.class.to_s == 'String' and string.include?('@')
    return string.delete('@')
  end
  string
end

def printhash(hash)
  puts '----------------------------------------------------'
  hash.each do |key,value|
    puts "#{key} => #{value}"
  end
end

def printarr(arr)
  arr.each do |a|
    if a.class.to_s == 'Hash'
      printhash(a) 
    else
      puts a
    end
  end
  puts '----------------------------------------------------'
end


# Object creation and cloning
@product = NewProduct.new
@copy_product = @product.deep_clone #This line has to be followed after every ActiveRecord object creation

#~ #Changes made to the object
#~ puts @product.product1.attr1.qqqq[1].wwww
@product.product1.attr1.qqqq[1].wwww = 2
@product.product1.attr1.qqqq[2].wwww = 3
@product.product2.attr2 = 4
#~ puts @product.product1.attr1.qqqq[1].wwww
##~ @p = Product.new

##~ p @p.attr2.class.to_s
  
printarr(audit(@copy_product, @product,'').flatten.to_a) # This method call has to be given after every successful creation/destruction/updation of an ActiveRecord Object

#~ p strip_at('@at')
